import socket
from . import reactor


class Channel(object):
    __slots__ = ["file", "owner", "_closed", "_write_buf"]
    IO_CHUNK = 8000
    def __init__(self, file, owner = None):
        self.file = file
        self._write_buf = ""
        self._closed = False
        self.owner = owner
    def close(self):
        if self._closed:
            return
        self.file.close()
        self._closed = True
    def fileno(self):
        return self.file.fileno()
    def on_read(self):
        if self._closed:
            self.disconnected()
            return False
        data = self._read()
        if not data:
            #self.close()
            self.disconnected()
            return False
        else:
            self.received(data)
            return True
    def on_write(self):
        if self._write_buf:
            self._write()
        return bool(self._write_buf)
    def write(self, data):
        if self._closed:
            raise EOFError("channel closed")
        if not self._write_buf:
            reactor.register_write(self.file, self.on_write)
        self._write_buf += data
    
    def disconnected(self):
        if self.owner:
            self.owner.disconnected(data)
    def received(self, data):
        if self.owner:
            self.owner.received(data)
    
    def _read(self):
        return self.file.read(self.IO_CHUNK)
    def _write(self):
        self.file.write(self._write_buf[:self.IO_CHUNK])
        self._write_buf = self._write_buf[self.IO_CHUNK:]

class SocketChannel(Channel):
    @classmethod
    def connect(cls, host, port):
        s = socket.socket()
        s.connect((host, port))
        return cls(s)
    def _read(self):
        return self.file.recv(self.IO_CHUNK)
    def _write(self):
        count = self.file.send(self._write_buf[:self.IO_CHUNK])
        self._write_buf = self._write_buf[count:]

class ListenerChannel(Channel):
    def __init__(self, file, factory):
        Channel.__init__(self, file)
        self.factory = factory
    @classmethod
    def create(cls, factory, port, host = "0.0.0.0", backlog = 8):
        s = socket.socket()
        s.bind((host, port))
        s.listen(backlog)
        return cls(s, factory)
    def _read(self):
        sock = self.file.accept()[0]
        chan = SocketChannel(sock)
        proto = self.factory()
        proto.bind(chan)
        return True
    def _write(self):
        raise NotImplementedError()




